package edu.nf.aop.context;

import edu.nf.aop.annotation.Around;
import edu.nf.aop.annotation.Aspect;
import edu.nf.aop.JoinPoint;

import java.lang.reflect.Method;
import java.util.Stack;

/**
 * @author wangl
 * @date 2021/9/24
 * 回调上下文
 */
public class ProceedingJoinPoint implements JoinPoint {

    /**
     * 目标对象
     */
    protected Object target;
    /**
     * 目标方法
     */
    protected Method method;
    /**
     * 目标方法参数
     */
    protected Object[] parameters;

    /**
     * 获取目标对象的所有参数
     */
    @Override
    public Object[] getArguments() {
        return parameters;
    }

    /**
     * 获取当前调用的方法
     */
    @Override
    public Method getMethod() {
        return method;
    }

    /**
     * 环绕通知栈
     */
    private final Stack<AspectInfo> stack = new Stack<>();

    public ProceedingJoinPoint(Object target, Method method, Object[] parameters) {
        this.target = target;
        this.method = method;
        this.parameters = parameters;
        initAdviceStack();
    }

    /**
     * 解析目标中的配置的拦截器注解，并返回拦截器栈
     */
    private void initAdviceStack() {
        //解析拦截器类上的注解
        if(method.getDeclaringClass().isAnnotationPresent(Aspect.class)){
            resolve(method.getDeclaringClass().getAnnotation(Aspect.class));
        }
        //解析拦截器方法上的注解
        if (method.isAnnotationPresent(Aspect.class)) {
            resolve(method.getAnnotation(Aspect.class));
        }
    }

    /**
     *  解析Interceptors注解中的切面信息并将通知存入Stack中
     */
    private void resolve(Aspect annotation){
        // 获取注解中配置的所有拦截器Class对象
        Class<?>[] aspectsClass = annotation.value();
        // 迭代数组，获取每一个拦截器
        for (Class<?> aspectClass : aspectsClass) {
            //创建切面实例
            Object aspect = newAspectInstance(aspectClass);
            for (Method advice : aspectClass.getMethods()) {
                if (advice.isAnnotationPresent(Around.class)){
                    AspectInfo adviceInfo = new AspectInfo(advice, aspect);
                    //将通知描述存入Stack
                    stack.push(adviceInfo);
                }
            }
        }
    }

    /**
     * 创建切面实例
     */
    private Object newAspectInstance(Class<?> aspectClass) {
        try {
            return aspectClass.getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            throw new RuntimeException("Create aspect instance fail.", e);
        }
    }

    /**
     * 调用拦截器栈
     */
    @Override
    public Object proceed() throws Throwable {
        // 如果环绕通知栈不为空，则执行栈中的通知方法
        if (!stack.empty()) {
            AspectInfo adviceInfo = stack.pop();
            return adviceInfo.advice().invoke(adviceInfo.aspect(), this);
        }
        // 拦截器栈执行完成后，则继续调用目标对象的方法
        return method.invoke(target, parameters);
    }
}
